package roborally.program;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;
import be.kuleuven.cs.som.annotate.Raw;

import roborally.program.command.Command;
import roborally.program.text.TextCommandPattern;
import roborally.program.text.TextFactory;
import roborally.program.text.TextManipulation;

/**
 * This class manages the different commands in the java program and their procedures for executing. Also this class manages the
 * different factories for creating a program from text input.
 * This class uses the design pattern singleton.
 * 
 * @invar The textCommandPatterns of this must be valid.
 * 		| hasProperTextCommandPatterns()
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 1.0
 */
public class CommandManager {

	/**
	 * Returns the only commandManager object of this class.
	 */
	@Basic @Immutable @Raw
	public static CommandManager get() {
		return commandManager;
	}
	
	/**
	 * The only object of this class, the commandManager.
	 */
	private static final CommandManager commandManager = new CommandManager();
	
	/**
	 * Creates a new commandManager object. The method asks for all the factories to return their TextCommandPattern.
	 * 
	 * @post For all the elements in the TextFactory enum, the pattern will be added to this.
	 */
	private CommandManager() {
		for(TextFactory factory : TextFactory.values())
			patterns.add(factory.getPattern());
	}
	
	/**
	 * Returns a Set containing all the TextCommandPatterns this commandManager manages.
	 */
	@Basic @Raw @Immutable
	public Set<TextCommandPattern> getTextCommandPatterns() {
		return Collections.unmodifiableSet(patterns);
	}
	
	/**
	 * Controls if the commandManager has valid textCommandPatterns.
	 * 
	 * @return True if all the textCommandPatterns are not null.
	 */
	@Raw @Immutable
	public boolean hasProperTextCommandPatterns() {
		for(TextCommandPattern lusPattern : getTextCommandPatterns())
			if(lusPattern == null)
				return false;
		return true;
	}
	
	/**
	 * A set with all the TextCommandPatterns, created by the different factories.
	 */
	private final Set<TextCommandPattern> patterns = new HashSet<TextCommandPattern>();
	
	/**
	 * Creates a new list of programs form the given String as input.
	 * 
	 * @param word The word of which the commands should be created.
	 * @return A list containing the java object representation of the given String as input.
	 * This forms an isomorfism between the String-representation and the java representation with objects of classes.
	 * @throws IllegalArgumentException
	 * 		When it is impossible to create a valid list of Commands from the given String.
	 */
	public List<Command> create(String word) throws IllegalArgumentException {
		List<Command> commands = new ArrayList<Command>();
		word = TextManipulation.removeRedundantSymbols(word);
		for(String block : TextManipulation.getBlocks(word)) {
			if(Pattern.matches(EMPTY_PATTERN, block))
				continue;
			else if(Pattern.matches(SUB_BLOCK, block)) // for example in (seq ( (move)) (turn)) there the ( (move)) is a subblock
				commands.addAll(create(block.substring(1, block.length() - 1))); // the first and last character are removed, the ( and ) so the result =  (move)
			else
				commands.add(createCommand(block));
		}
		return commands;
	}
	
	/**
	 * Creates a command of the given word. The word must be a textual representation of 
	 * just one valid command.
	 * 
	 * @param block The input String of which a command must be created.
	 * @return A valid command of which the given block is a textual representation.
	 * @throws IllegalArgumentException
	 * 		If the input block doesn't match any of the known patterns of all valid commands.
	 */
	public Command createCommand(String block) throws IllegalArgumentException {
		for(TextCommandPattern lusPattern : patterns)
			if(lusPattern.matches(block)) {
				return lusPattern.create(block);
			}		
		throw new IllegalArgumentException("No pattern matched the given input");
	}
	
	/**
	 * A String pattern for an empty String.
	 */
	private final String EMPTY_PATTERN = "[\\s]*";
	
	/**
	 * A String pattern for a subblock. Something like <(> <wit karakter(s)>  <(> <willekeurig karakter(s)> <)> <wit karakter(s)>  <)>
	 * as for example in (seq ( (move)) (turn)) there the ( (move)) is a subblock.
	 * @note When the input is something of the form (seq ( (move) ) ), then without this pattern, the input would be seen as a false
	 * command.
	 * @note Do not confuse this subblock with the subblock of TextManipulation!
	 */
	private static final String SUB_BLOCK = "(\\()(\\s*)(\\()(.*)(\\))(\\s*)(\\))";
											//  (  [   ]   (  ...   )  [   ]   ) with [   ] a random number white spaces
	
}
